﻿using System.Collections.Generic;

namespace LxCFD
{
    public partial class LinearSolverBase
    {
        protected int MaxCoarseLevel = 0;
        protected List<AMGInZone> AMGsInZone = new List<AMGInZone>();
        protected List<AMGInContact> AMGsInContact = new List<AMGInContact>();
        protected int[] VariableCount { get; set; }
        public void SolveByJacobi<T1, T2>(List<T1> zones, List<T2> contacts) where T1 : IJacobiSolvableZone where T2 : ISolvableContact
        {
            if (contacts.Count > 0)
            {
                MultiThreads.RunCalTasks((taskIndex) => { foreach (var c in contacts) { c.Copy_xArray(taskIndex); } });
            }
            MultiThreads.RunCalTasks((taskIndex) => { foreach (var z in zones) { z.CalByJacobi_Step1(taskIndex); } });
            if (contacts.Count > 0)
            {
                MultiThreads.RunCalTasks((taskIndex) => { foreach (var c in contacts) { c.Copy_xCommonArray(taskIndex); } });
            }
            MultiThreads.RunCalTasks((taskIndex) => { foreach (var z in zones) { z.CalByJacobi_Step2(taskIndex); } });
        }
        public void CalResidual<T1, T2>(List<T1> zones, List<T2> contacts) where T1 : ICalResidual where T2 : ISolvableContact
        {
            MultiThreads.RunCalTasks((taskIndex) => { foreach (var c in contacts) { c.Copy_xArray(taskIndex); } });
            MultiThreads.RunCalTasks((taskIndex) => { foreach (var z in zones) { z.CalResidual(taskIndex); } });
        }
    }
    public partial class LinearSolverBase
    {
        private volatile int AMG_V_StartLevel;//对于V循环，该值为-1
        private void iniVStartLevel()
        {
            AMG_V_StartLevel = AMGBase.SolvingLevel;
        }
        protected void AMG_V<T>(List<T> connects) where T : ISolvableContact
        {
            while (AMGBase.SolvingLevel < MaxCoarseLevel)
            {
                iniAndRestrict(connects);
                SolveByBiCG(AMGsInZone, AMGsInContact);
            }
            while (AMGBase.SolvingLevel > AMG_V_StartLevel)
            {
                SolveByJacobi(AMGsInZone, AMGsInContact);
                prolong();
            }
        }
        protected void AMG_F<T>(List<T> connects) where T : ISolvableContact
        {
            while (AMGBase.SolvingLevel < MaxCoarseLevel)
            {
                iniAndRestrict(connects);
            }
            SolveByBiCG(AMGsInZone, AMGsInContact);
            prolong();
            iniVStartLevel();
            setIsUpdateAFalse();
            while (AMG_V_StartLevel >= 0)
            {
                SolveByBiCG(AMGsInZone, AMGsInContact);
                AMG_V(connects);
                SolveByBiCG(AMGsInZone, AMGsInContact);
                prolong();
                iniVStartLevel();
            }
        }
        private void iniAndRestrict<T>(List<T> connects) where T : ISolvableContact
        {
            if (AMGBase.SolvingLevel == -1)
            {
                if(connects .Count > 0)
                {
                    MultiThreads.RunCalTasks((taskIndex) => { foreach (var c in connects) { c.Copy_xArray(taskIndex); } });
                }
            }
            else
            {
                if(AMGsInContact.Count > 0)
                {
                    MultiThreads.RunCalTasks((taskIndex) => { foreach (var c in AMGsInContact) { c.Copy_xArray(taskIndex); } });
                }
            }
            ++AMGBase.SolvingLevel;
            MultiThreads.RunCalTasks((taskIndex) =>
            {
                foreach (var z in AMGsInZone)
                {
                    z.Restrict(taskIndex);
                }
                foreach (var c in AMGsInContact)
                {
                    c.Restrict(taskIndex);
                }
            });
        }
        private void prolong()
        {
            MultiThreads.RunCalTasks((taskIndex) =>
            {
                foreach (var z in AMGsInZone)
                {
                    z.Prolong(taskIndex);
                }
            });
            --AMGBase.SolvingLevel;
        }
        private void setIsUpdateAFalse()
        {
            foreach (var z in AMGsInZone) { z.isNeededUpdateA = false; }//更新一次后，后面不需要更新了
            foreach (var c in AMGsInContact) { c.isNeededUpdateA = false; }
        }
        protected void LinkAMG<T1, T2>(List<T1> scalars, List<T2> connects) where T1 : IAMGSolvableZone where T2 : IAMGSolvableContact
        {
            if (AMGsInZone.Count != scalars.Count || AMGsInContact.Count != connects.Count) LxConsole.Error("AMG求解器和待求解方程数量不同");
            for (int i1 = 0; i1 < AMGsInZone.Count; i1++)
            {
                AMGsInZone[i1].LinkToMatrix(scalars[i1]);
            }
            for (int i1 = 0; i1 < AMGsInContact.Count; i1++)
            {
                AMGsInContact[i1].LinkToMatrix(connects[i1]);
            }
            AMGBase.SolvingLevel = -1;
            iniVStartLevel();
        }
    }
    public partial class LinearSolverBase
    {
        private volatile int BiCGIteNum = 8;
        public void SolveByBiCG<T1, T2>(List<T1> zones, List<T2> contacts) where T1 : IBiCGStabSolvableZone, IJacobiSolvableZone where T2 : IBiCGStabSolvableContact, ISolvableContact
        {
            if(contacts .Count > 0)
            {
                MultiThreads.RunCalTasks((taskIndex) => { foreach (var c in contacts) { c.Copy_xArray(taskIndex); } });
            }     
            MultiThreads.RunCalTasks((taskIndex) => { foreach (var z in zones) { z.Initialize_Step1_r0rp1rho(taskIndex); } });            
            if (BiCGBase.Initialize_Step2())
            {
                SolveByJacobi(zones, contacts);
            }
            else
            {
                for (int i1 = 1; ; i1++)
                {
                    MultiThreads.RunCalTasks((taskIndex) => { foreach (var z in zones) { z.Cal_yArray(taskIndex); } });
                    MultiThreads.RunCalTasks((taskIndex) => { foreach (var c in contacts) { c.Copy_yArray(taskIndex); } });
                    MultiThreads.RunCalTasks((taskIndex) => { foreach (var z in zones) { z.Cal_vArray_alpha1(taskIndex); } });
                    if (BiCGBase.Cal_alpha()) return;
                    MultiThreads.RunCalTasks((taskIndex) => { foreach (var z in zones) { z.Cal_sArray(taskIndex); } });
                    MultiThreads.RunCalTasks((taskIndex) => { foreach (var z in zones) { z.Cal_zArray(taskIndex); } });
                    MultiThreads.RunCalTasks((taskIndex) => { foreach (var c in contacts) { c.Copy_zArray(taskIndex); } });
                    MultiThreads.RunCalTasks((taskIndex) => { foreach (var z in zones) { z.Cal_tArray_omega1(taskIndex); } });
                    if (BiCGBase.Cal_omega()) return;
                    MultiThreads.RunCalTasks((taskIndex) => { foreach (var z in zones) { z.Cal_xArray(taskIndex); } });
                    //检查收敛
                    if (i1 >= BiCGIteNum) { return; }
                    MultiThreads.RunCalTasks((taskIndex) => { foreach (var z in zones) { z.Cal_rArray(taskIndex); } });
                    MultiThreads.RunCalTasks((taskIndex) => { foreach (var z in zones) { z.Cal_rhoPart1(taskIndex); } });
                    if (BiCGBase.Cal_rhobeta()) return;
                    MultiThreads.RunCalTasks((taskIndex) => { foreach (var z in zones) { z.Cal_pArray(taskIndex); } });
                }
            }

        }
    }
}
//public void Clear()
//{
//    maxCoarseLevel = 0;
//    AMGsInZone.Clear();
//    AMGsInContact.Clear();
//    if (variableCount != null)
//    {
//        for (int i1 = 0; i1 < variableCount.Length; i1++)
//        {
//            variableCount[i1] = 0;
//        }
//    }
//}